
import { ICatchAwait, IThrottle, IDebounce, IDefer, IRequestFrame } from "./types"

export const throttle: IThrottle = (fn, time) => {
    let _timer = null;
    return (...args) => {
        if (_timer) return;
        _timer = setTimeout(() => {
            fn.call(this, ...args);
            _timer = null;
        }, time);
    };
};

export const debounce: IDebounce = (fn, time) => {
    let _timer = null;
    return (...args) => {
        if (_timer) {
            clearTimeout(_timer);
            _timer = null;
        }
        _timer = setTimeout(() => {
            fn.call(this, ...args);
        }, time);
    };
}

export const defer: IDefer = (timer = 0) => {
    let resolve, reject
    return {
        promise: new Promise<void>((_resolve, _reject) => {
            resolve = _resolve
            reject = _reject
            if (timer > 0) setTimeout(() => reject("timeout"), timer)
        }),
        resolve, reject
    }
}

export const catchAwait: ICatchAwait<Promise<any>> = (defer) => defer.then(res => [null, res]).catch(err => [err])

export const requestFrame: IRequestFrame = (callback, delay = 0) => {
    if (!callback) throw Error("callback is empty")
    const isNodeEnv = typeof process !== "undefined"
    const frameFn = isNodeEnv ? setImmediate : requestAnimationFrame
    const _p = performance
    let start = _p.now()
    let freeze = false
    const clear = () => freeze = true
    const _tempCb = () => {
        if (freeze) return
        const diff = _p.now() - start
        if (diff >= delay) {
            start = _p.now()
            callback(start)
        }
        frameFn(_tempCb)
    }
    _tempCb()
    return clear
}

export default {
    throttle,
    debounce,
    defer,
    catchAwait,
}